﻿
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Data.Linq;
using System.Data.Linq.Mapping;
using System.Runtime.Serialization;

using SharedGenomics.Core;

namespace SharedGenomics.Domain
{
    [Table(Name = "dbo.FileSchemaColumn")]
    [DataContract]
    [InheritanceMapping(Code = "0", Type = typeof(FileSchemaColumn), IsDefault = true)]
    [KnownType(typeof(NominalFileSchemaColumn))]
    [InheritanceMapping(Code = "2", Type = typeof(NominalFileSchemaColumn))]
    [KnownType(typeof(OrdinalFileSchemaColumn))]
    [InheritanceMapping(Code = "3", Type = typeof(OrdinalFileSchemaColumn))]
    [KnownType(typeof(GenderFileSchemaColumn))]
    [InheritanceMapping(Code = "5", Type = typeof(GenderFileSchemaColumn))]
    [KnownType(typeof(IdFileSchemaColumn))]
    [InheritanceMapping(Code = "4", Type = typeof(IdFileSchemaColumn))]
    [KnownType(typeof(ScaleFileSchemaColumn))]
    [InheritanceMapping(Code = "1", Type = typeof(ScaleFileSchemaColumn))]
    public class FileSchemaColumn
    {
        private EntitySet<FileSchemaColumnValue> _FileSchemaColumnValues;
        private EntityRef<FileSchema> _FileSchema;

        public FileSchemaColumn()
        {
            this.Initialize();
        }

        protected FileSchemaColumn(ColumnDistribution distrbution)
            : this()
        {
            this.Distribution = distrbution;
        }

        [Column(AutoSync = AutoSync.OnInsert, DbType = "Int NOT NULL IDENTITY", IsPrimaryKey = true, IsDbGenerated = true)]
        [DataMember(Order = 1)]
        public int Id { get; set; }

        [Column(DbType = "Int NOT NULL")]
        [DataMember(Order = 2)]
        public int SchemaId { get; set; }

        [Association(Name = "FileSchema_FileSchemaColumn", Storage = "_FileSchema", ThisKey = "SchemaId", OtherKey = "Id", IsForeignKey = true)]
        public FileSchema FileSchema
        {
            get { return this._FileSchema.Entity; }
            set
            {
                FileSchema previousValue = this._FileSchema.Entity;
                if (((previousValue != value)
                            || (this._FileSchema.HasLoadedOrAssignedValue == false)))
                {
                    if ((previousValue != null))
                    {
                        this._FileSchema.Entity = null;
                        previousValue.FileSchemaColumns.Remove(this);
                    }
                    this._FileSchema.Entity = value;
                    if ((value != null))
                    {
                        value.FileSchemaColumns.Add(this);
                        this.SchemaId = value.Id;
                    }
                    else
                    {
                        this.SchemaId = default(int);
                    }
                }
            }
        }

        [Column(DbType = "SmallInt NOT NULL")]
        [DataMember(Order = 3)]
        public short Idx { get; set; }

        [Column(DbType = "VarChar(200) NOT NULL", CanBeNull = false)]
        [DataMember(Order = 4)]
        public string Name { get; set; }

        [Column(DbType = "VarChar(200)")]
        [DataMember(Order = 5)]
        public string Label { get; set; }

        [Column(DbType = "SmallInt NOT NULL", IsDiscriminator = true)]
        [DataMember(Order = 6)]
        internal short CategoryType { get; set; }

        [Association(Name = "FileSchemaColumn_FileSchemaColumnValue", Storage = "_FileSchemaColumnValues", ThisKey = "Id", OtherKey = "ColumnId")]
        [DataMember(Order = 7, EmitDefaultValue = false)]
        public EntitySet<FileSchemaColumnValue> FileSchemaColumnValues
        {
            get { return this._FileSchemaColumnValues; }
            set { this._FileSchemaColumnValues.Assign(value); }
        }

        protected virtual void Initialize()
        {
            this._FileSchemaColumnValues = new EntitySet<FileSchemaColumnValue>(new Action<FileSchemaColumnValue>(v => v.Column = this), new Action<FileSchemaColumnValue>(v => v.Column = null));
        }

        #region Public Methods

        public virtual void Validate(string value, ValidationMessageCollection validationMessages)
        {
            //subclasses should override and add any validation errors to the collection...
        }

        public virtual object Parse(string value)
        {
            //throw NotImplementedException?
            return null;
        }

        public override string ToString()
        {
            return this.Label ?? this.Name;
        }

        #endregion

        #region Protected Methods

        protected void AddValue(string value, string label, bool missing)
        {
            if (value == null)
                throw new ArgumentNullException("value");

            string trimmedValue = value.Trim();
            string trimmedLabel = label == null ? null : label.Trim();

            //check the given value has not already been added
            FileSchemaColumnValue existingValue = this.FileSchemaColumnValues.Where(cv => cv.Value.Equals(value, StringComparison.InvariantCultureIgnoreCase)).SingleOrDefault();
            if (existingValue != null)
            {
                throw new ArgumentException(String.Format("Value {0} has already been added to this column", value));
            }

            //create new column value and add to the collection
            FileSchemaColumnValue val = new FileSchemaColumnValue()
            {
                Value = trimmedValue,
                Label = trimmedLabel,
                IsMissing = missing
            };

            this.FileSchemaColumnValues.Add(val);
        }

        #endregion

        #region Properties

        public ColumnDistribution Distribution
        {
            get { return (ColumnDistribution)this.CategoryType; }
            set
            {
                MethodArgumentValidator.ValidateEnum<ColumnDistribution>(value);

                this.CategoryType = (short)value;
            }
        }

        protected IEnumerable<string> MissingValuesCore
        {
            get
            {
                return from cv in this.FileSchemaColumnValues
                       where cv.IsMissing
                       select cv.Value;
            }
        }

        public virtual bool IsValidPhenotypeColumn
        {
            get
            {
                //subclasses should override this property...
                return false;
            }
        }

        #endregion
    }
}
